#include "expense.hpp"
#include "allowance_type.hpp"
#include "activity.hpp"
#include "file_list.hpp"

#include <algorithm>
#include <functional>


expense_csv::expense_csv(): file(file_list::expense_filename()) { }

expense_csv::~expense_csv() {}

std::pair<float,float> const expense_csv::report_expense_type(int _empl_id, int _allowance_type_id, 
								   date _first_date, date _last_date, bool _include_last_date, std::ofstream* _fp)
{
	std::vector<int> activity_list;
	std::multimap<int, date> activity_date_relation;
	std::multimap<int, std::pair<float, float> > activity_refund_relation;
	//std::multimap<int, expense_struct_csv > activity_expense_relation;
	std::multimap< std::pair<int, date>, expense_struct_csv > activity_expense_relation;

	std::pair<float, float> total_expense_amount = expense_csv::get_expense_type(_empl_id, _allowance_type_id, activity_list, activity_date_relation, 
		activity_refund_relation, activity_expense_relation, _first_date,_last_date, _include_last_date);
		

	
	if(_fp && (total_expense_amount.first + total_expense_amount.second))
	{
		//*_fp << static_cast<allowance_type_struct_csv>(allowance_type_csv::find(_allowance_type_id)).name() << ", "; // allowance type name
		//*_fp << static_cast<allowance_type_struct_csv>(allowance_type_csv::find(_allowance_type_id)).descr() << ", "; // allowance type name
		*_fp << std::endl << static_cast<allowance_type_struct_csv>(allowance_type_csv::find(_allowance_type_id)).descr() << std::endl; // allowance type name

		for(std::vector<int>::iterator _it = activity_list.begin(); _it != activity_list.end(); _it++)
		{						

			std::pair<std::multimap<int,std::pair<float,float> >::iterator,multimap<int,std::pair<float,float> >::iterator> ii;
			ii = activity_refund_relation.equal_range(*_it);

			float total_refund(0.), total_not_refund(0.);

			// we can iterate over a range just like with begin() and end()
			for(std::multimap<int, std::pair<float, float> >::iterator i = ii.first; i != ii.second; ++i ) 
			{
				total_refund += i->second.first;
				total_not_refund += i->second.second;
			}
			*_fp << total_refund << ", " << total_not_refund << ", "; // total amount related to activity
			*_fp << static_cast<activity_struct_csv>(activity_csv::find(*_it)).name() << ", "; // activity name

			std::pair<std::multimap<int, date>::iterator, std::multimap<int, date>::iterator > ij;
			ij = activity_date_relation.equal_range(*_it);
			for(std::multimap<int, date>::iterator k = ij.first; k != ij.second; ++k)
			{
				*_fp << "\n\t" << k->second ; // dates
				//*_fp << ", " << static_cast<activity_struct_csv>(activity_csv::find(*_it)).name() << ", "; // activity name

				bool is_different_date(true);
				std::pair< std::multimap< std::pair<int, date>, expense_struct_csv>::iterator, std::multimap< std::pair<int, date>, expense_struct_csv>::iterator > kj;
				kj = activity_expense_relation.equal_range(std::make_pair<int, date>(*_it, k->second));
				for(std::multimap< std::pair<int,date>, expense_struct_csv>::iterator k = kj.first; k != kj.second; ++k)
				{	
					if(!is_different_date)
					{
						*_fp << std::endl <<"\t\t\t";
					}
					else
					{
						*_fp << "\t";
					}
					if(is_different_date) is_different_date = false;
				
					*_fp  << k->second.amount() << ", " << k->second.descr(); 
				}
			}
			*_fp << std::endl ;
		}
		*_fp << std::endl ;
	}

	return total_expense_amount;
}


std::pair<float, float> const expense_csv::get_expense_type(int _empl_id, int _allowance_type_id, std::vector<int>& activity_list, 
					std::multimap<int, date>& activity_date_relation, 
					std::multimap<int, std::pair<float, float> >& activity_refund_relation,
					std::multimap< std::pair<int, date>, expense_struct_csv>& activity_expense_relation,
					date _first_date, date _last_date, bool _include_last_date)
{
	bool is_empty = true;
	float differential_daily_expense_type[] = {0, 0}, 
		global_daily_expense_type[] = {0, 0};

	int activity_id;	

	date date_ref(not_a_date_time), date_null(not_a_date_time);
	
	csv_struct csv_file(file_list::expense_filename());
	while(_allowance_type_id && !csv_file.eof())
	{
		++csv_file;
		expense_struct_csv row(csv_file.get_line());
			
		if(row.allowance_id() != _allowance_type_id) continue;

		if(!check_parm(row, _empl_id, _first_date, _last_date, _include_last_date)) continue;

		activity_expense_relation.insert(std::make_pair< std::pair<int, date>, expense_struct_csv const>(std::make_pair<int, date>(row.activity_id(), row.day()), row));				
		
		if((row.day() == date_ref && row.activity_id() == activity_id) || date_ref == date_null)
		{
			if(date_ref == date_null)
			{
				date_ref = row.day();
				activity_id = row.activity_id();

				activity_list.push_back(activity_id);
				activity_date_relation.insert(std::make_pair<int, date>(activity_id, date_ref));
			}

			row.find_daily_expenses(differential_daily_expense_type);
			if(is_empty) is_empty = false;
			continue;
		}				
		
		activity_refund_relation.insert(std::make_pair<int, std::pair<float, float> >(activity_id, 
				std::make_pair<float, float>(differential_daily_expense_type[0], differential_daily_expense_type[1])));

		global_daily_expense_type[0] += differential_daily_expense_type[0];
		global_daily_expense_type[1] += differential_daily_expense_type[1];

		date_ref = row.day();

		if(activity_id != row.activity_id())
		{
			activity_id = row.activity_id();
			
			if(find_if(activity_list.begin(), activity_list.end(), bind2nd(equal_to<int>(), activity_id)) ==  activity_list.end())
			{
				activity_list.push_back(activity_id);
			}
		}

		// toDO: da inserire nell'elenco solo se non esiste gia' la coppia (id, data)
		if(find_if(activity_date_relation.begin(), activity_date_relation.end(), 
			bind2nd(equal_to< std::pair<int, date> >(), std::make_pair<int, date>(activity_id, date_ref))) == activity_date_relation.end())
		{
			activity_date_relation.insert(std::make_pair<int, date>(activity_id, date_ref));
		}

		differential_daily_expense_type[0] = differential_daily_expense_type[1] = 0;
		row.find_daily_expenses(differential_daily_expense_type);

	}	

	if(!is_empty)
	{
		activity_refund_relation.insert(std::make_pair<int, std::pair<float, float> >(activity_id, 
				std::make_pair<float, float>(differential_daily_expense_type[0], differential_daily_expense_type[1])));
		global_daily_expense_type[0] += differential_daily_expense_type[0];
		global_daily_expense_type[1] += differential_daily_expense_type[1];
	}

    return std::make_pair<float, float>(global_daily_expense_type[0], global_daily_expense_type[1]);
}


std::pair<float, float> const expense_csv::report_expenses(int _empl_id, date _first_date, date _last_date, bool _include_last_date, std::ofstream* _fp)
{
	
	std::pair<float, float> grand_amount;

	csv_struct csv_file(file_list::allowance_type_filename());

	while(!csv_file.eof())
	{
		++csv_file;
		allowance_type_struct_csv row(csv_file.get_line());

		if(row.id())
		{
			std::pair<float, float> grand_amount_tmp = report_expense_type(_empl_id, row.id(), _first_date, _last_date, _include_last_date, _fp);

			if(_fp && (grand_amount_tmp.first + grand_amount_tmp.second))
			{
				*_fp << grand_amount_tmp.first << ", " <<  grand_amount_tmp.second << " = totale " <<  static_cast<allowance_type_struct_csv>(allowance_type_csv::find(row.id())).descr()<< std::endl;
			}
			grand_amount.first += grand_amount_tmp.first;
			grand_amount.second += grand_amount_tmp.second;
		}
	}

	if(_fp && (grand_amount.first + grand_amount.second))
	{
		//*_fp << "totale = " << (grand_amount.first + grand_amount.second) << std::endl;
		*_fp << std::endl << grand_amount.first << ", " <<  grand_amount.second << " = totale" << std::endl;
	}

	return grand_amount;
}

/*
float expense_csv::expense_report(int _empl_id, date _first_date, date _last_date, bool _include_last_date, std::ofstream* _fp)
{
	csv_struct csv_file(file_list::expense_filename());
	float *current_expenses = new float[allowance_type_csv::num()+1];
	float *cumulative_expenses = new float[allowance_type_csv::num()+1];

	date date_ref(not_a_date_time), date_null(not_a_date_time);
	int activity_id;
	
	bool is_empty(true);
	float amount=0;

	init_daily_expenses(current_expenses);
	init_daily_expenses(cumulative_expenses);

	while(!csv_file.eof())
	{
		++csv_file;
		expense_struct_csv row(csv_file.get_line());
			
		if(!check_parm(row, _empl_id, _first_date, _last_date, _include_last_date)) continue;

		if((row.day() == date_ref && row.activity_id() == activity_id) || date_ref == date_null)
		{
			if(date_ref == date_null)
			{
				date_ref = row.day();
				activity_id = row.activity_id();
			}
			row.current_expenses(current_expenses);
			continue;
		}

		amount +=  expense_csv::report_daily_expenses(current_expenses, date_ref, activity_id, _fp);
		expense_csv::sum_daily_expenses(current_expenses, cumulative_expenses);

		date_ref = row.day();
		activity_id = row.activity_id();

		init_daily_expenses(current_expenses);
		row.current_expenses(current_expenses);

		if(is_empty) is_empty = false;
	}	

	if(!is_empty)
	{
		amount += report_daily_expenses(current_expenses, date_ref, activity_id, _fp);
		expense_csv::sum_daily_expenses(current_expenses, cumulative_expenses);
		report_daily_expenses(cumulative_expenses, date_null, 0, _fp);
	}

	delete current_expenses;
	current_expenses =NULL;

	delete cumulative_expenses;
	cumulative_expenses = NULL;

	return amount;
}

void expense_csv::sum_daily_expenses(float _daily_expenses[], float _cumulative_expenses[])
{
	for(unsigned short j=0; j <= allowance_type_csv::num(); j++)
    {		
		_cumulative_expenses[j] += _daily_expenses[j];
    }
}

void expense_csv::init_daily_expenses(float _daily_expenses[])
{
	for(unsigned short j=0; j <= allowance_type_csv::num(); j++)
    {
		_daily_expenses[j] =0.;
    }
}


float expense_csv::report_daily_type_expenses(float _daily_type_expenses_refund, float _daily_type_expenses_no_refund, date _date_ref, int _activity_id, std::ofstream* _fp)
{
	float tot(0.);
	date date_null(not_a_date_time);
        


	for(unsigned short j =0; !is_open && j<= max_allowance_type_num; j++)
    {		
		if(!j) std::cout << "\t";
		std::cout << "\t" << static_cast<allowance_type_struct_csv>(allowance_type_csv::find(j)).name(); 
		if(!_fp) continue;		

		if(!j) *_fp << "\t";
		*_fp << "\t" << static_cast<allowance_type_struct_csv>(allowance_type_csv::find(j)).name();
	}
	if(!is_open) is_open = true;

	std::cout << std::endl;
	
	
	if(_date_ref != date_null)
	{
		std::cout << _date_ref << "\t"; //<< static_cast<activity_struct_csv>(activity_csv::find(_activity_id)).name();
	}else{
		std::cout << "\t";
	}
	if(_fp)
    {
		*_fp << std::endl;
		if(_date_ref != date_null)
		{
			*_fp << _date_ref << "\t"; //<< static_cast<activity_struct_csv>(activity_csv::find(_activity_id)).name();
		}else{
			*_fp << "\t";
		}
    }
	
	for(unsigned short j =0; j<= max_allowance_type_num; j++)
    {						
		tot += _daily_expenses[j];
	    std::cout << "\t";
		if(_daily_expenses[j]) std::cout << _daily_expenses[j];
		if(!_fp) continue;
		*_fp << "\t" ;
		if(_daily_expenses[j]) *_fp << _daily_expenses[j] ;		
	}
			          	
	return tot;
}



float expense_csv::report_daily_expenses(float* _daily_expenses, date _date_ref, int _activity_id, std::ofstream* _fp)
{
	float tot(0.);
	unsigned short max_allowance_type_num(allowance_type_csv::num());
	static bool is_open;
	date date_null(not_a_date_time);
         
	for(unsigned short j =0; !is_open && j<= max_allowance_type_num; j++)
    {		
		if(!j) std::cout << "\t";
		std::cout << "\t" << static_cast<allowance_type_struct_csv>(allowance_type_csv::find(j)).name(); 
		if(!_fp) continue;		

		if(!j) *_fp << "\t";
		*_fp << "\t" << static_cast<allowance_type_struct_csv>(allowance_type_csv::find(j)).name();
	}
	if(!is_open) is_open = true;

	std::cout << std::endl;
	if(_date_ref != date_null)
	{
		std::cout << _date_ref << "\t"; //<< static_cast<activity_struct_csv>(activity_csv::find(_activity_id)).name();
	}else{
		std::cout << "\t";
	}
	if(_fp)
    {
		*_fp << std::endl;
		if(_date_ref != date_null)
		{
			*_fp << _date_ref << "\t"; //<< static_cast<activity_struct_csv>(activity_csv::find(_activity_id)).name();
		}else{
			*_fp << "\t";
		}
    }
	
	for(unsigned short j =0; j<= max_allowance_type_num; j++)
    {						
		tot += _daily_expenses[j];
	    std::cout << "\t";
		if(_daily_expenses[j]) std::cout << _daily_expenses[j];
		if(!_fp) continue;
		*_fp << "\t" ;
		if(_daily_expenses[j]) *_fp << _daily_expenses[j] ;		
	}
			          	
	return tot;
}

*/
bool expense_csv::check_parm(expense_struct_csv& row,
		int _empl_id, date _first_date, date _last_date, bool _include_last_date)
{
	//if(row.empl_id() != _empl_id){ return false;} 					// check empl id, it's always me!
	if(!row.id()) { return false; }
	if(row.day() < _first_date) { return false; }                        // discard any date before
	if(row.day() > _last_date ){ return false; }                         // discard any date after
	if(row.day() == _last_date && !_include_last_date){  return false; } // is the last date to be considered?
		
	return true;
}
